//
// Created by Tony on 2024/6/13.
//

#ifndef RUNTIME_INSTRUCTION_H
#define RUNTIME_INSTRUCTION_H

#include "../ByteCodeReader.h"
#include "../rt_data/Frame.h"
#include <cstdint>

namespace Runtime {

using Common::OpCode;
using std::string;

class Instruction {
    const char* mName;
    OpCode mOpCode;

protected:
    static constexpr int WIDTH_OP_NAME = 16;
    static constexpr int WIDTH_UINT16 = 8;

public:
    Instruction(const char* name, OpCode opcode) : mName(name), mOpCode(opcode) {}
    virtual ~Instruction() = default;

    const char* name() { return mName; }
    OpCode opcode() { return mOpCode; }

    virtual void fetchOperands(ByteCodeReader& reader) = 0;
    virtual void execute(RtData::Frame& frame) = 0;
    virtual string toString() = 0;
};

class NoOperandsInstruction : public Instruction {
public:
    explicit NoOperandsInstruction(const char* name, OpCode opcode) : Instruction(name, opcode) {}

    void fetchOperands(ByteCodeReader& reader) override {}
    string toString() override;
};

class Index16Instruction : public Instruction {
protected:
    uint16_t mIndex = 0;

public:
    explicit Index16Instruction(const char* name, OpCode opcode) : Instruction(name, opcode) {}

    void fetchOperands(ByteCodeReader& reader) override { mIndex = reader.readUint16(); }
    string toString() override;
};

class BranchInstruction : public Instruction {
protected:
    static const uint16_t INSTR_LEN = 3; // bytes
    int16_t mOffset = 0;

public:
    explicit BranchInstruction(const char* name, OpCode opcode) : Instruction(name, opcode) {}

    void fetchOperands(ByteCodeReader& reader) override { mOffset = reader.readInt16(); }
    string toString() override;
};

class CompareInstruction : public NoOperandsInstruction {
protected:
    static RtTaggedValue cmp(RtTaggedValue a, RtTaggedValue b);

public:
    explicit CompareInstruction(const char* name, OpCode opcode)
        : NoOperandsInstruction(name, opcode) {}
};

void initClass(RtData::Frame& frame, const shared_ptr<RtCode::Class>& clazz);

} // namespace Runtime

#endif // RUNTIME_INSTRUCTION_H
